//
//  LGBluetoothCentralManager.m
//  SportRing
//
//  Created by mykoma on 14-4-5.
//  Copyright (c) 2014年 HotOne. All rights reserved.
//

#import "LGBluetoothCentralManager.h"
#import "YMSCBCentralManager.h"
#import "YMSCBService.h"
#import "YMSCBCharacteristic.h"

@interface LGBluetoothCentralManager () <CBPeripheralDelegate>

// 因为在搜索到设备的时候, 通一个设备, 有可能会出发当前的回调几次, 所以这里做了一个保护
@property (nonatomic, strong) NSMutableDictionary * ymcbPeripherals;
@property (nonatomic, strong) NSMutableDictionary * ymcbCharacteristics;
@property (nonatomic, strong) YMSCBCentralManager * ymcbManager;
@property (nonatomic, strong) YMSCBPeripheral * ymcbPeripheral;
@property (nonatomic, strong) NSTimer * watchDog;
@property (nonatomic, copy) CompletionBlock actionBlock;
@end

@implementation LGBluetoothCentralManager

CWL_SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(LGBluetoothCentralManager, sharedInstance);

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.ymcbCharacteristics = [NSMutableDictionary dictionary];
        self.ymcbPeripherals = [NSMutableDictionary dictionary];
        
        self.ymcbManager = [[YMSCBCentralManager alloc] initWithKnownPeripheralNames:nil
                                                                               queue:nil
                                                                useStoredPeripherals:YES
                                                                            delegate:self];
    }
    return self;
}

-(void)setup
{
    __weak LGBluetoothCentralManager * weakSelf = self;
    [self.ymcbManager scanForPeripheralsWithServices:nil
                                             options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(NO)}
                                           withBlock:^(CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI, NSError *error)
     {
         __block BOOL isMeetPrefixName = NO;
         NSArray * filterPrefixNames = [self.delegate deviceFilterPrefixName];
         NSAssert(filterPrefixNames.count > 0, nil);
         [filterPrefixNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
          {
              if ([peripheral.name hasPrefix:obj])
              {
                  isMeetPrefixName = YES;
                  *stop = YES;
              }
          }];
         if (isMeetPrefixName)
         {
             // 因为在搜索到设备的时候, 通一个设备, 有可能会出发当前的回调几次, 所以这里做了一个保护
             YMSCBPeripheral * ymcbPeripheral = weakSelf.ymcbPeripherals[peripheral];
             if (ymcbPeripheral == nil)
             {
                 ymcbPeripheral = [[YMSCBPeripheral alloc] initWithPeripheral:peripheral
                                                                      central:weakSelf.ymcbManager
                                                                       baseHi:0
                                                                       baseLo:0];
                 ymcbPeripheral.delegate = weakSelf;
                 
                 weakSelf.ymcbPeripherals[peripheral] = ymcbPeripheral;
                 [weakSelf.ymcbManager addPeripheral:ymcbPeripheral];
                 
                 if ([weakSelf.delegate respondsToSelector:@selector(didFindPeripheral:)]) {
                     [weakSelf.delegate didFindPeripheral:ymcbPeripheral];
                 }
             }
         }
     }];
}

- (void)setNotifyValue:(BOOL)enabled
 forCharacteristicUUID:(CBUUID *)uuid
             withValue:(NSData *)data
            completion:(CompletionBlock)block
{
    self.actionBlock = block;
    [self.watchDog invalidate];
    self.watchDog = [self timerForCloseWatchFlag];
    YMSCBCharacteristic * characteristic = self.ymcbCharacteristics[uuid];
    // Workaround, 新设备读取通知必须先setNotifyValue:YES一次.
    [characteristic setNotifyValue:YES
                         withBlock:^(NSError *error)
     {}];
    [characteristic writeValue:data
                     withBlock:^(NSError *error)
     {}];
    __block LGBluetoothCentralManager * weakSelf = self;
    __weak YMSCBCharacteristic * weakChar = characteristic;
    [characteristic setNotifyValue:YES
                         withBlock:^(NSError *error)
     {
         [weakSelf.watchDog invalidate];
         block(weakChar.cbCharacteristic);
     }];
}

-(void)updateCharacteristicUUID:(CBUUID *)uuid
                      withValue:(NSData *)data
                     completion:(CompletionBlock)block
{
    self.actionBlock = block;
    [self.watchDog invalidate];
    self.watchDog = nil;
    self.watchDog = [self timerForCloseWatchFlag];
    __block LGBluetoothCentralManager * weakSelf = self;
    __weak YMSCBCharacteristic * characteristic = self.ymcbCharacteristics[uuid];
    [characteristic writeValue:data
                     withBlock:^(NSError *error)
     {
         NSLog(@"Bluetooth write response: %@", characteristic.cbCharacteristic.value);
         [weakSelf.watchDog invalidate];
         
         NSLog(@"======= 数据返回 销毁 timer %p", weakSelf.watchDog);
         weakSelf.watchDog = nil;
         NSLog(@"======= 数据返回 销毁 timer %p", weakSelf.watchDog);
         if (weakSelf.actionBlock) {
             weakSelf.actionBlock([NSData data]);
             weakSelf.actionBlock = nil;
         }
     }];
}

-(void)readValueForCharacteristicUUID:(CBUUID *)uuid
                           completion:(CompletionBlock)block
{
    self.actionBlock = block;
    [self.watchDog invalidate];
    self.watchDog = [self timerForCloseWatchFlag];
    __block LGBluetoothCentralManager * weakSelf = self;
    YMSCBCharacteristic * characteristic = self.ymcbCharacteristics[uuid];
    [characteristic readValueWithBlock:^(NSData *data, NSError *error)
     {
         [weakSelf.watchDog invalidate];
         weakSelf.actionBlock(data);
     }];
}

-(void)connectTo:(YMSCBPeripheral *)peripheral withBlock:(CompletionBlock)block
{
    self.ymcbPeripheral = peripheral;
    self.actionBlock = block;
    [self.watchDog invalidate];
    self.watchDog = [self timerForCloseWatchFlag];
    
    __block LGBluetoothCentralManager * weakSelf = self;
    CompletionBlock completionBlock = ^(id data)
    {
        [weakSelf.watchDog invalidate];
        weakSelf.actionBlock(data);
        
    };
    [self connectPeripheral:self.ymcbPeripheral withBlock:completionBlock];
}

-(void)listenForUUID:(CBUUID *)uuid
               block:(CompletionBlock)block
{
    YMSCBCharacteristic * characteristic = self.ymcbCharacteristics[uuid];
    
    __weak YMSCBCharacteristic * weakChar = characteristic;
    [characteristic setNotifyValue:YES
                         withBlock:^(NSError *error)
     {
         block(weakChar.cbCharacteristic);
     }];
}

-(void)disconnectWithDevice
{
    [self cleanConnection];
}

-(void)connectPeripheral:(YMSCBPeripheral *)ymcbPeripheral withBlock:(CompletionBlock)block
{
    __weak LGBluetoothCentralManager * weakSelf = self;
    [self.ymcbPeripheral resetWatchdog];
    [ymcbPeripheral connectWithOptions:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@(YES),
                                         CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}
                             withBlock:^(YMSCBPeripheral *yp, NSError *error)
     {
         NSDictionary * serviceDict = [weakSelf.delegate validServicesUUIDs];
         
         //组合需要处理的servicesUUID
         NSMutableDictionary * mDict = [NSMutableDictionary dictionary];
         [serviceDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
          {
              YMSCBService * service = [weakSelf construtYMSCBServiceWithKey:key
                                                                        data:obj
                                                                      parent:yp];
              mDict[service.uuid] = service;
          }];
         yp.serviceDict = [mDict copy];
         [yp discoverServices:nil
                    withBlock:^(NSArray *services, NSError *error)
          {
              if (services.count == 0)
              {
                  block(nil);
                  return ;
              }
              
              NSUInteger MAX_SERVICE = services.count;
              __block int counter = 0;
              // 没有必要对每一个service都去查询他的Characteristic, 因为在目前的设备上, 会一次性把可用的都返回了.
              [services enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
               {
                   
                   YMSCBService * ymcbService = obj;
                   
                   NSDictionary * validCharacteristics = [weakSelf.delegate validCharacteristics];
                   NSMutableDictionary * mcDict = [NSMutableDictionary dictionary];
                   [validCharacteristics enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
                    {
                        CBUUID * uuid = [CBUUID UUIDWithData:obj];
                        YMSCBCharacteristic * ymcbCharacteristic = [[YMSCBCharacteristic alloc] initWithName:key
                                                                                                      parent:yp
                                                                                                        uuid:uuid
                                                                                                      offset:0];
                        mcDict[ymcbCharacteristic.uuid] = ymcbCharacteristic;
                    }];
                   
                   ymcbService.characteristicDict = mcDict;
                   [ymcbService discoverCharacteristics:nil
                                              withBlock:^(NSDictionary *chDict, NSError * error)
                    {
                        counter ++;
                        [chDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
                         {
                             YMSCBCharacteristic * tempChar = obj;
                             if (tempChar.cbCharacteristic)
                             {
                                 NSLog(@"Bluetooth: found characteristic: %@", tempChar.cbCharacteristic.UUID);
                                 weakSelf.ymcbCharacteristics[key] = obj;
                             }
                         }];
                        // 如果每个service都搜索了它的Characteristic, 那么回调
                        if (counter == MAX_SERVICE)
                        {
                            block([NSData data]);
                        }
                    }];
               }];
          }];
     }];
}

#pragma mark - CBCentralManagerDelegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
        {
            [self setup];
        }
            break;
        default:
            break;
    }
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    [self.delegate didDisconnectWithDevice];
}

#pragma mark - Private

-(NSTimer *)timerForCloseWatchFlag
{
    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:20
                                                       target:self
                                                     selector:@selector(closeWatchFlag)
                                                     userInfo:nil
                                                      repeats:NO];
    NSLog(@"======= 启动 timer %p", timer);
    return timer;
}

-(void)closeWatchFlag
{
    NSLog(@"=======>? timer 超时   %p", self.watchDog);
    [self.watchDog invalidate];
    [self cleanConnection];
    if (self.actionBlock) {
        self.actionBlock(nil);
    }
}

-(void)cleanConnection
{
    [self.ymcbPeripheral cancelConnection];
    [self.ymcbPeripherals removeAllObjects];
    [self.ymcbCharacteristics removeAllObjects];
}

-(NSData *)reverseData:(NSData *)data
{
    NSUInteger length = [data length];
    char * bytes = (char *)[data bytes];
    for (int i = 0; i < length / 2; i ++)
    {
        char byte = bytes[i];
        bytes[i] = bytes[length - i - 1];
        bytes[length - i - 1] = byte;
    }
    return [NSData dataWithBytes:bytes length:length];
}

-(YMSCBService *)construtYMSCBServiceWithKey:(NSString *)key
                                        data:(NSData *)uuidData
                                      parent:(YMSCBPeripheral *)parent
{
    NSUInteger length = [uuidData length];
    if (length == 16)
    {
        NSUInteger half = length / 2;
        NSData * serviceUUIDData = [self reverseData:uuidData];
        const void * bytes = [serviceUUIDData bytes];
        int64_t lo = 0;
        memcpy(&lo, bytes, half);
        int64_t hi = 0;
        memcpy(&hi, bytes + half, half);
        return [[YMSCBService alloc] initWithName:key
                                           parent:parent
                                           baseHi:hi
                                           baseLo:lo
                                    serviceOffset:0];
    }
    else
    {
        NSData * serviceUUIDData = [self reverseData:uuidData];
        const void * bytes = [serviceUUIDData bytes];
        unsigned short offset = 0;
        memcpy(&offset, bytes, length);
        return [[YMSCBService alloc] initWithName:key
                                           parent:parent
                                           baseHi:0
                                           baseLo:0
                                    serviceOffset:offset];
    }
}

@end

